package org.xx.armory.swing.components;

import org.xx.armory.swing.UIControllers;
import org.xx.armory.swing.builders.UIModelClass;

import javax.swing.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UncheckedIOException;

import static java.awt.Toolkit.getDefaultToolkit;

/**
 * 错误对话框。
 */
@UIModelClass
public class ErrorDialog<T extends Throwable>
        extends DialogPane {
    private final T throwable;

    public ErrorDialog(
            T throwable
    ) {
        this.throwable = throwable;

        controller().readOnly("detail", true);
        controller().readOnly("more", true);
        controller().hide("more");

        final JTextArea detailPane = controller().get(JTextArea.class, "detail");
        detailPane.setColumns(getDetailColumns());
        detailPane.setRows(getDetailRows());
        final int preferredWidth = detailPane.getPreferredSize().width;

        controller().setText("message",
                             "<html><body style=\"margin:0;padding:0;width:" + preferredWidth + "px;\" >" + getMessage() + "</body></html>");
        final String detail = getDetail();
        if (!detail.isEmpty()) {
            controller().setText("detail", getDetail());
        } else {
            controller().hide("detail");
        }

        controller().disable("copy-more");

        controller().connect("show-more", this::showMore);
        controller().connect("copy-more", this::copyMore);
    }

    protected int getDetailColumns() {
        return 40;
    }

    protected int getDetailRows() {
        return 4;
    }

    protected int getMoreColumns() {
        return 40;
    }

    protected int getMoreRows() {
        return 8;
    }

    /**
     * 获取错误对话框关联的异常。
     *
     * @return 错误对话框关联的异常。
     */
    protected T getThrowable() {
        return this.throwable;
    }

    /**
     * 获取错误消息。
     *
     * @return 错误消息。
     */
    protected String getMessage() {
        if (this.throwable != null) {
            return UIControllers.STANDARD.getMessage("generic-error");
        } else {
            return "Throwable is null";
        }
    }

    /**
     * 获取更详细的错误信息。
     * <p>通常是错误的调用栈。</p>
     *
     * @return 更详细的错误信息。
     */
    protected String getMore() {
        if (this.throwable != null) {
            try (final StringWriter bufferWriter = new StringWriter()) {
                this.throwable.printStackTrace(new PrintWriter(bufferWriter, true));
                bufferWriter.flush();
                return bufferWriter.toString();
            } catch (IOException ex) {
                throw new UncheckedIOException(ex);
            }
        } else {
            return "";
        }
    }

    /**
     * 获取错误的明细。
     *
     * @return 错误的明细。
     */
    protected String getDetail() {
        if (this.throwable != null) {
            return this.throwable.getMessage();
        } else {
            return "";
        }
    }

    private void showMore(
            ActionEvent event
    ) {
        controller().disable("show-more");

        final JTextArea morePane = controller().get(JTextArea.class, "more");
        morePane.setColumns(getMoreColumns());
        morePane.setRows(getMoreRows());

        controller().setText("more", getMore());
        controller().show("more");
        controller().enable("copy-more");

        pack();
    }

    private void copyMore(
            ActionEvent event
    ) {
        final Clipboard clipboard = getDefaultToolkit().getSystemClipboard();
        clipboard.setContents(new StringSelection(controller().getText("more")), null);
    }
}
